package com.itmhw.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hmall.common.client.ItemClient;
import com.hmall.common.dto.Item;
import com.itmhw.entity.PageDTO;
import com.itmhw.entity.RequesParams;
import com.itmhw.entity.itemDto;
import com.itmhw.service.SearchService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author mhw
 * @since 2022/12/8 23:36
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ItemClient itemClient;

    @Override
    public void insertById(Long id) {
        try {
            // 0.根据id调用远程查询数据
            Item item = itemClient.byItemId(id);
            // 转换为文档类型
            itemDto itemDto = new itemDto(item);
            // 1.准备Request对象
            IndexRequest request = new IndexRequest("hotel").id(id.toString());
            // 2.准备Json文档
            request.source(JSON.toJSONString(itemDto), XContentType.JSON);
            // 3.发送请求
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteById(Long id) {
        try {
            // 1.准备Request
            DeleteRequest request = new DeleteRequest("item",id.toString());
            // 2.发送请求
            client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 自动补全
     * @param key
     * @return
     */
    @Override
    public List<String> select(String key) {
        try {
            //创建对象
            SearchRequest searchRequest = new SearchRequest("item");
            //组织DSL语句
            searchRequest.source().suggest(new SuggestBuilder().addSuggestion("title_suggest",
                    SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(key)
                            .skipDuplicates(true)
                            .size(10)
            ));
            //发送请求
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            // 4.解析结果
            Suggest suggest = response.getSuggest();
            // 4.1.根据补全查询名称，获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("title_suggest");
            // 4.2.获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            // 4.3.遍历
            List<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        }catch (IOException e){
            throw  new RuntimeException(e);
        }
    }


    /**
     * 实现搜索中的基本查询
     * @param requesParams
     * @return
     */
    @Override
    public PageDTO<itemDto> pageDTO(RequesParams requesParams) {

        try {
            //分页结果
            PageDTO<itemDto> pageDTO = new PageDTO<>();
            //创建请求
            SearchRequest request = new SearchRequest("item");
            //关键字查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            if (StringUtils.isBlank(requesParams.getKey())){
                boolQuery.must(QueryBuilders.matchAllQuery());
            }else {
                //request.source().query(QueryBuilders.matchQuery("all",requesParams.getKey()));
                boolQuery.must(QueryBuilders.matchQuery("all",requesParams.getKey()));
            }
            //brand查询，使用must算分查询
            if (StringUtils.isNotBlank(requesParams.getBrand())){
                boolQuery.must(QueryBuilders.termQuery("brand",requesParams.getBrand()));
            }
            //category查询，使用filter不算分查询
            if (StringUtils.isNotEmpty(requesParams.getCategory())){
                boolQuery.filter(QueryBuilders.termQuery("category",requesParams.getCategory()));
            }
            //price查询，使用范围查询
            if (requesParams.getMaxPrice()!=null&requesParams.getMinprice()!=null){
                boolQuery.filter(QueryBuilders.rangeQuery("price").gt(requesParams.getMinprice()).lt(requesParams.getMaxPrice()));
            }
            //排序
            if( "score".equals(requesParams.getSortBy()) ){
                request.source().sort("score", SortOrder.DESC);
            }else if( "price".equals(requesParams.getSortBy()) ){
                request.source().sort("price",SortOrder.ASC);
            }
            //分页
            request.source().from( (requesParams.getPage()-1)*requesParams.getSize() ).size( requesParams.getSize() );
            //高亮
            request.source().highlighter(
                    //new HighlightBuilder().field("name").requireFieldMatch(false)
                    new HighlightBuilder().field("all").requireFieldMatch(true)
            );
            request.source().query(boolQuery);

            //执行请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析响应
            SearchHits searchHits = response.getHits();
            long total = searchHits.getTotalHits().value;
            pageDTO.setTotal(total); //文档总数量

            List<itemDto> itemDtos = new ArrayList<>();
            SearchHit[] hits = searchHits.getHits();
            for (SearchHit hit : hits) {
                String json = hit.getSourceAsString();
                itemDto itemDto = JSONObject.parseObject(json, itemDto.class);
                //高亮
                Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
                HighlightField highlightField = highlightFieldMap.get("category");
                if( highlightField!=null && highlightField.getFragments().length>0){
                    String highLightName = highlightField.getFragments()[0].string();
                    System.out.println("高亮处理后的酒店名称: " + highLightName);
                    itemDto.setName(highLightName);
                }
                itemDtos.add(itemDto);
            }
            pageDTO.setList(itemDtos);//当前页的酒店数据明细

            return pageDTO;
        }catch (IOException e) {
            throw  new RuntimeException(e);
        }
    }
    /**
     * 解析查询结果
     * @param response
     */
    private PageDTO handleResponse(SearchResponse response){
        //解析响应结果
        SearchHits hits = response.getHits();
        //查询总条数
        long total = hits.getTotalHits().value;
        System.out.println("total = " + total);
        //查询分结果数组
        SearchHit[] hitsHits = hits.getHits();
        //建立HotelDoc集合
        List<itemDto> hotelDocList = new ArrayList<>();
        for (SearchHit hitsHit : hitsHits) {
            //得到source
            String json = hitsHit.getSourceAsString();
            //打印
            System.out.println("json = " + json);
            //将json转为实体类
            itemDto hotelDoc = JSON.parseObject(json, itemDto.class);
            //将实体类放入集合中
            hotelDocList.add(hotelDoc);
        }
        return new PageDTO(total,hotelDocList);
    }


    @Override
    public Map<String, List<String>> getFilters(RequesParams requesParams) {
        try {
            //创建对象
            SearchRequest request = new SearchRequest("item");
            request.source().size(0);
            //实现聚合
            request.source().aggregation(AggregationBuilders
                    .terms("brandAgg")
                    .field("brand")
                    .size(10)
            );


            request.source().aggregation(AggregationBuilders
                    .terms("categoryAgg")
                    .field("category")
                    .size(10)
            );
            //发送请求
            SearchResponse search = client.search(request, RequestOptions.DEFAULT);
            //解析
            Map<String,List<String>> result = new HashMap<>();
            List<String> brandList = fetchAggs(search, "brandAgg");
            List<String> categoryList = fetchAggs(search, "categoryAgg");
            result.put("brand",brandList);
            result.put("category",categoryList);
            return result;
        } catch (IOException e) {
            throw  new RuntimeException(e);
        }
    }
    private List<String> fetchAggs(SearchResponse search,String aggName) {
        List<String> list = new ArrayList<>();
        Aggregations aggregations = search.getAggregations();
        Terms brandAgg = aggregations.get(aggName);
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString();
            list.add(key);
        }
        return list;
    }
}
